

#include "JNIHelper.h"
extern "C" {
#include "libavcodec/jni.h"
}

JNIHelper *JNIHelper::pInstance = nullptr;

JNIHelper *JNIHelper::Instance() {
  if (pInstance) {
    pInstance = new JNIHelper();
  }
  return pInstance;
}

void JNIHelper::Destroy() {
  DELETE_PTR(pInstance)
}

JNIHelper::~JNIHelper() {
  auto pEnv = getEnv();
  if (pEnv) {
    pEnv->DeleteGlobalRef(_pContext);
  }
}

void JNIHelper::setJavaVM(JavaVM *pVM) {
  _pJavaVM = pVM;
  av_jni_set_java_vm(_pJavaVM, nullptr);
}

JavaVM *JNIHelper::getJavaVM() {
  return _pJavaVM;
}

void JNIHelper::setContext(jobject pContext) {
  auto pEnv = getEnv();
  _pContext = pEnv->NewGlobalRef(pContext);
}

jobject JNIHelper::getContext() {
  return _pContext;
}

JNIEnv *JNIHelper::getEnv(jint version) {
  if (_pJavaVM) {
    JNIEnv *pEnv = nullptr;
    if (_pJavaVM->GetEnv(reinterpret_cast<void **>(&pEnv), version) == JNI_OK) {
      return pEnv;
    }
  }

  return nullptr;
}

/**
 * JNI中可以使用pthread或者std::thread创建线程，因为线程并不是从Java环境中创建的，所以创建的线程没有JNIEnv，如果需要
 * 使用JNIEnv，则需要调用JavaVM的AttachCurrentThread将当前线程附加到虚拟机上，并创建本地引用表
 * 注意：AttachCurrentThread可以调用多次，第一次会将当前线程附加到虚拟机上并返回JNIEnv，之后调用直接返回JNIEnv
 */
JNIEnv *JNIHelper::attachThread() {
  if (_pJavaVM) {
    JNIEnv *pEnv = nullptr;
    if (_pJavaVM->AttachCurrentThread(&pEnv, nullptr) == JNI_OK) {
      return pEnv;
    }
  }
  return nullptr;
}

/**
 * 调用DetachCurrentThread会释放本地引用表
 */
void JNIHelper::detachThread() {
  if (_pJavaVM) {
    _pJavaVM->DetachCurrentThread();
  }
}